package optimizers.localsearch.cmaes;

import java.util.HashMap;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import optimizers.cmaes.CMAEvolutionStrategy;
import optimizers.cmaes.fitness.IObjectiveFunction;
import optimizers.commons.RunConfig;
import optimizers.commons.evaluator.FitnessEvaluationService;
import optimizers.ganeat.Constants;
import optimizers.ganeat.square.GASquare;
import optimizers.ganeat.square.IndividualSquare;
import optimizers.localsearch.AbstractParamOptimizer;
import optimizers.tools.FileManager;
import optimizers.tools.Misc;
import runconfiguration.SquareConfiguration;
import util.MyUtil;
import visualization.GraphDisplayer;
import visualization.PlotExpData;
import model.OligoSystemGeneral;
import fitness.Fitness;
import fitness.FitnessResult;

public class CMAES extends AbstractParamOptimizer {
	CMAEvolutionStrategy cma;

	private OligoSystemGeneral getOligoModel(double[] x) {
		System.out.println(x.length);
		double[][][] template = new double[templateSystem.nSimpleSequences + 1][templateSystem.nSimpleSequences][templateSystem.nSimpleSequences];
		double[][] seqK = new double[templateSystem.nSimpleSequences + 1][templateSystem.nSimpleSequences];
		double[][] seqC = new double[templateSystem.nSimpleSequences + 1][templateSystem.nSimpleSequences];
		double[][] inhK = new double[templateSystem.nSimpleSequences + 1][templateSystem.nSimpleSequences];

		int count = 0;

		for (int i = 0; i < templateSystem.nSimpleSequences + 1; i++) {
			for (int j = 0; j < templateSystem.nSimpleSequences; j++) {
				for (int k = 0; k < templateSystem.nSimpleSequences; k++) {
					if (config.template.optimizeMechanism == RunConfig.TEMPLATE_OPTIMIZE_YES
							&& this.templateSystem.templates[i][j][k] > 0) {
						template[i][j][k] = x[count];
						count++;
					} else {
						template[i][j][k] = this.templateSystem.templates[i][j][k];
					}
				}
			}
		}

		for (int i = 0; i < templateSystem.nSimpleSequences; i++) {
			if (config.seqK.optimizeMechanism == RunConfig.SEQK_OPTIMIZE_YES) {
				seqK[0][i] = x[count];
				count++;
			} else {
				seqK[0][i] = this.templateSystem.seqK[0][i];
			}
		}

		for (int i = 0; i < templateSystem.nSimpleSequences + 1; i++) {
			for (int j = 0; j < templateSystem.nSimpleSequences; j++) {
				if (config.seqC.optimizeMechanism == RunConfig.SEQC_OPTIMIZE_YES
						&& this.templateSystem.seqConcentration[i][j] > 0) {
					seqC[i][j] = x[count];
					count++;
				} else {
					seqC[i][j] = this.templateSystem.seqConcentration[i][j];
				}
			}
		}

		for (int i = 0; i < templateSystem.nSimpleSequences; i++) {
			for (int j = 0; j < templateSystem.nSimpleSequences; j++) {
				if (config.inhK.optimizeMechanism == RunConfig.INHK_OPTIMIZE_YES
						&& this.templateSystem.inhK[i + 1][j] > 0) {
					inhK[i + 1][j] = x[count];
					count++;
				} else if (config.inhK.optimizeMechanism == RunConfig.INHK_OPTIMIZE_NO_7_6_RULE
						&& this.templateSystem.inhK[i + 1][j] > 0) {
					inhK[i + 1][j] = MyUtil.calculateInhK(seqK[0][i],
							seqK[0][j]);
				} else {
					inhK[i + 1][j] = this.templateSystem.inhK[i + 1][j];
				}
				seqK[i + 1][j] = inhK[i + 1][j] / 3;
			}
		}

		double exo = 0;
		if (config.exo.optimizeMechanism == RunConfig.EXO_OPTIMIZE_YES) {
			exo = x[count];
			count++;
		} else {
			exo = this.templateSystem.exo;
		}

		OligoSystemGeneral oligoSystem = new OligoSystemGeneral(
				templateSystem.nSimpleSequences, template, seqK, inhK, seqC,
				exo, this.templateSystem.intToName,
				this.templateSystem.nameToInt);
		return oligoSystem;
	}

	private int getDimenstion() {
		int count = 0;
		if (config.template.optimizeMechanism == RunConfig.TEMPLATE_OPTIMIZE_YES) {
			for (int i = 0; i < templateSystem.nSimpleSequences + 1; i++) {
				for (int j = 0; j < templateSystem.nSimpleSequences; j++) {
					for (int k = 0; k < templateSystem.nSimpleSequences; k++) {
						if (this.templateSystem.templates[i][j][k] > 0) {
							count++;
						}
					}
				}
			}
		}
		if (config.seqK.optimizeMechanism == RunConfig.SEQK_OPTIMIZE_YES) {
			count += this.templateSystem.nSimpleSequences;
		}
		if (config.seqC.optimizeMechanism == RunConfig.SEQC_OPTIMIZE_YES) {
			for (int i = 0; i < templateSystem.nSimpleSequences + 1; i++) {
				for (int j = 0; j < templateSystem.nSimpleSequences; j++) {
					if (this.templateSystem.seqConcentration[i][j] > 0) {
						count++;
					}
				}
			}
		}
		if (config.inhK.optimizeMechanism == RunConfig.INHK_OPTIMIZE_YES) {
			for (int i = 0; i < templateSystem.nSimpleSequences; i++) {
				for (int j = 0; j < templateSystem.nSimpleSequences; j++) {
					if (this.templateSystem.inhK[i + 1][j] > 0) {
						count++;
					}
				}
			}
		}
		if (config.exo.optimizeMechanism == RunConfig.EXO_OPTIMIZE_YES) {
			count++;
		}
		return count;
	}

	public CMAES(OligoSystemGeneral templateSystem,
			CompletionService<Object> cservice, int maxRunTimeInSeconds,
			RunConfig config) {
		super(templateSystem, cservice, maxRunTimeInSeconds, config, false);
		cma = new CMAEvolutionStrategy();
		cma.readProperties("src/optimizers/config/CMAES-DNA-20130207-Huy.properties");

		cma.setDimension(getDimenstion()); // overwrite
		// some
		// loaded
		// properties

		cma.setInitialX(0.0); // in each dimension, also setTypicalX can be used
		cma.setInitialStandardDeviation(1.5); // 0.15 // also a mandatory
												// setting
	}

	@Override
	public OligoSystemGeneral[] createInitGeneration(
			OligoSystemGeneral oligoSystem) {
		double[][] pop = cma.samplePopulation();
		OligoSystemGeneral[] systems = new OligoSystemGeneral[pop.length];
		for (int i = 0; i < pop.length; i++) {
			pop[i] = cma.resampleSingle(i);
			systems[i] = getOligoModel(pop[i]);
		}
		return systems;
	}

	@Override
	public boolean stopEvolution() {
		return (cma.stopConditions.getNumber() > 0);
	}

	@Override
	public OligoSystemGeneral[] getNextGeneration(OligoSystemGeneral[] curGen,
			double[] fitness) {
		cma.updateDistribution(fitness);
		double[][] pop = cma.samplePopulation();
		OligoSystemGeneral[] systems = new OligoSystemGeneral[pop.length];
		for (int i = 0; i < pop.length; i++) {
			pop[i] = cma.resampleSingle(i);
			systems[i] = getOligoModel(pop[i]);
		}
		return systems;
	}

	public static void main(String[] args) {
		ExecutorService eservice = Executors
				.newFixedThreadPool(Constants.nParallelThreads);
		CompletionService<Object> cservice = new ExecutorCompletionService<Object>(
				eservice);
		RunConfig config = new SquareConfiguration();
		IndividualSquare templateIndividual = new IndividualSquare(
				new HashMap<String, Integer>(), new HashMap<String, String>(),
				-1, 0, config, "Node gens:\r\n" + " a 20.0431 20\r\n"
						+ " c 58.6952 1\r\n" + " Icc 0.587 1\r\n"
						+ " i 25.2767 1\r\n" + " n 24.1125 1\r\n"
						+ " Inn 0.2411 1\r\n" + " Iii 0.2528 1\r\n"
						+ "Connection gens:\r\n" + " 1 a->a false 3.7959\r\n"
						+ " 2 a->c false 0.2367\r\n"
						+ " 3 c->a false 4.6433\r\n"
						+ " 9 c->c true 21.0849\r\n"
						+ " 10 a->Icc true 23.968\r\n"
						+ " 33 c->i false 2.5251\r\n"
						+ " 34 i->a true 2.9932\r\n"
						+ " 43 i->i true 55.7416\r\n"
						+ " 56 c->n false 0.358\r\n"
						+ " 57 n->i false 0.3764\r\n"
						+ " 63 n->n true 14.9429\r\n"
						+ " 64 c->Inn true 43.8504\r\n"
						+ " 69 n->Iii true 42.127\r\n" + "Exo: 0\r\n"
						+ "Pol: 0\r\n" + "Nick: 0 ");

		OligoSystemGeneral templateSystem = templateIndividual.getOligoModel();
		CMAES cmaes = new CMAES(templateSystem, cservice, 300, config);
		OligoSystemGeneral optimizedSystem = cmaes.optimize();
	}
}
